package fr.labri.macchiato.ui.cart;

import java.util.List;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.TextBox;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.widgets.IconButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;

import fr.labri.macchiato.core.model.Item;
import fr.labri.macchiato.core.model.Cart;
import fr.labri.macchiato.core.model.Article;
import fr.labri.macchiato.core.model.Offer;
import fr.labri.macchiato.core.model.Product;
import fr.labri.macchiato.core.service.ProjectService;
import fr.labri.macchiato.core.service.Services;
import fr.labri.macchiato.core.technical.Callback;
import fr.labri.macchiato.core.technical.MObservable;
import fr.labri.macchiato.core.technical.MObserver;
import fr.labri.macchiato.ui.cart.events.ValidateSearchHandler;

/**
 * <p>
 * The widget which represent the cart <br>
 * May be used to directly add generic article to the cart or drag model and
 * offer from the search widget
 * </p>
 * 
 * 
 * @author ALASSANE SESSOUMA
 * 
 */
public class CartWidget extends VLayout implements MObserver {

	private static final String FIND_OFFERS_TOOLTIP = "Rechercher automatiquement les meilleures offres";

	private static final String FIND_OFFERS_ICON = "/icons/zoom.png";

	private static final String WATERMARK_STYLE = "watermark";

	private static final String ADD_ARTICLE_WATERMARK = "Entrez le nom d'un article, puis pressez Entrée";

	private static final String CART_LINE_STYLE = "cartLine";
	private static final String CART_LINE_NOTFOUND_STYLE = "cartNotfoundLine";

	private TextBox addArticleTextBox;
	private VLayout articleLayout;
	private VLayout modelLayout;
	private VLayout offerLayout;
	private HLayout footer;

	private Label totalLabel;

	private String cartName;
	private String projectName;

	/**
	 * A Widget displaying the cart from a project
	 * 
	 * @param projectName
	 * @param cartName
	 */
	public CartWidget(final String projectName, final String cartName) {

		this.cartName = cartName;
		this.projectName = projectName;

		getProjectService().getCategory(projectName, cartName).addObserver(this);

		setAutoHeight();

		initAddArticleTextBox();
		articleLayout = new VLayout();
		articleLayout.setHeight("*");
		addMember(articleLayout);

		modelLayout = new VLayout();
		modelLayout.setHeight("*");
		addMember(modelLayout);

		offerLayout = new VLayout();
		offerLayout.setHeight("*");
		addMember(offerLayout);

		footer = new HLayout();
		footer.setStyleName(CART_LINE_STYLE);
		footer.setWidth100();
		addMember(footer);

		IconButton findOffersButton = new IconButton("");
		findOffersButton.setTooltip(FIND_OFFERS_TOOLTIP);
		findOffersButton.setLargeIcon(FIND_OFFERS_ICON);
		findOffersButton.setOrientation("vertical");
		findOffersButton.addClickHandler(new ClickHandler() {

			public void onClick(ClickEvent event) {
				getProjectService().getBestOffers(projectName, cartName, new Callback<List<Product>>() {

					@Override
					public void onCallback(List<Product> result) {
						getProjectService().clearCategory(projectName, cartName);
						articleLayout.removeMembers(articleLayout.getMembers());
						modelLayout.removeMembers(modelLayout.getMembers());
						offerLayout.removeMembers(offerLayout.getMembers());
						getProjectService().addProducts(projectName, result,cartName);
					}
				});

			}
		});

		footer.addMember(findOffersButton);

		totalLabel = new Label();
		totalLabel.setAlign(Alignment.RIGHT);
		totalLabel.setStyleName("productLabel");
		footer.addMember(totalLabel);
		updateTotal();
		totalLabel.setWidth("*");

		loadProducts();

	}

	private ProjectService getProjectService() {
		return Services.getInstance().getProjectService();
	}

	/**
	 * Creates a new article and put it in the cart
	 */
	private void newArticle() {
		if (addArticleTextBox.getText() != null) {
			Item article = new Item(addArticleTextBox.getText());
			getProjectService().addProduct(projectName, article, cartName);
			addArticleTextBox.setText(null);
		}
	}
	
	/**
	 * Adds a product to the {@link CartWidget}
	 * 
	 * @param product
	 */
	private void addProductLine(Product product) {
		addProductLine(product, false);
	}
	
	private void addProductLine(Product product, boolean notFound) {
		
		if (product instanceof Item)
			addArticleLine((Item)product, notFound);
		else if (product instanceof Article)
			addModelLine((Article)product, notFound);
		else if (product instanceof Offer)
			addOfferLine((Offer)product);
	}

	/**
	 * Adds an article to the {@link CartWidget}
	 * 
	 * @param article
	 */
	private void addArticleLine(Item article, boolean notFound) {
		CartRow cartLine = new CartRow(article, cartName, projectName);
		article.addObserver(this);
		if (notFound)
			cartLine.setStyleName(CART_LINE_NOTFOUND_STYLE);
		articleLayout.addMember(cartLine);
		addArticleLineHandlers(article, cartLine);
	}

	private void addArticleLineHandlers(final Item article, final CartRow cartLine) {
		cartLine.addRemoveProductHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				removeProduct(cartLine, article);
			}
		});

		cartLine.addValidateHandler(new ValidateSearchHandler() {

			@Override
			public void fireEvent() {
				removeProduct(cartLine, article);

				for (Article model : getSelectedModels()) {
					model.setQuantity(article.getQuantity());
					getProjectService().addProduct(projectName, model, cartName);
				}
			}
		});
	}

	/**
	 * Adds a model to the {@link CartWidget}
	 * 
	 * @param model
	 */
	private void addModelLine(Article model, boolean notFound) {
		CartRow cartLine = new CartRow(model, cartName, projectName);
		model.addObserver(this);
		if (notFound)
			cartLine.setStyleName(CART_LINE_NOTFOUND_STYLE);
		modelLayout.addMember(cartLine);
		addModelLineHandlers(model, cartLine);
	}

	private void addModelLineHandlers(final Article model, final CartRow cartLine) {
		cartLine.addRemoveProductHandler(new ClickHandler() {

			public void onClick(ClickEvent event) {
				removeProduct(cartLine, model);

			}
		});

		cartLine.addValidateHandler(new ValidateSearchHandler() {

			@Override
			public void fireEvent() {
				removeProduct(cartLine, model);

				Offer offer = getSelectedOffer();
				offer.setQuantity(model.getQuantity());
				getProjectService().addProduct(projectName, offer, cartName);
			}
		});
	}
	
	/**
	 * Adds an offer to the {@link CartWidget}
	 * 
	 * @param offerEntry
	 */
	private void addOfferLine(Offer offerEntry) {
		CartRow offerRow = new CartRow(offerEntry, cartName, projectName);
		offerEntry.addObserver(this);
		offerLayout.addMember(offerRow);
		addOfferLineHandlers(offerEntry, offerRow);

		updateTotal();
	}

	private void addOfferLineHandlers(final Offer offerL, final CartRow offerRow) {
		offerRow.addRemoveProductHandler(new ClickHandler() {

			public void onClick(ClickEvent event) {
				removeProduct(offerRow, offerL);
				updateTotal();
			}
		});

		offerRow.addValidateHandler(new ValidateSearchHandler() {

			@Override
			public void fireEvent() {
				removeProduct(offerRow, offerL);

				Offer offer = getSelectedOffer();
				offer.setQuantity(offerL.getQuantity());
				getProjectService().addProduct(projectName, offer, cartName);
			}
		});
	}
	

	private void loadProducts() {
		// Load Articles
		List<Item> articles = getProjectService().getCategory(projectName, cartName).getArticles();
		for (Item articleEntry : articles)
			addArticleLine(articleEntry, false);

		// LoadModels
		List<Article> models = getProjectService().getCategory(projectName, cartName).getModels();
		for (Article cartEntry : models) {
			addModelLine(cartEntry, false);
		}

		// Load Offers
		List<Offer> offer = getProjectService().getCategory(projectName, cartName).getOffers();
		for (Offer cartEntry : offer) {
			addOfferLine(cartEntry);
		}
	}

	private void removeProduct(CartRow cartLine, Product product) {
		getProjectService().removeProduct(projectName, product, cartName);
		cartLine.destroy();
		updateTotal();
	}

	/**
	 * Initializes the input text box with the correct handlers and styles
	 */
	private void initAddArticleTextBox() {
		addArticleTextBox = new TextBox();
		addArticleTextBox.setStyleName(CART_LINE_STYLE);
		addArticleTextBox.setWidth("100%");

		setWatermark();
		addArticleTextBox.setHeight("3em");
		addMember(addArticleTextBox);

		addArticleTextBox.addFocusHandler(new FocusHandler() {

			public void onFocus(FocusEvent event) {
				addArticleTextBox.setStyleName(CART_LINE_STYLE);
				addArticleTextBox.setText("");

			}
		});

		addArticleTextBox.addBlurHandler(new BlurHandler() {

			public void onBlur(BlurEvent event) {
				setWatermark();

			}
		});

		addArticleTextBox.addKeyDownHandler(new KeyDownHandler() {
			public void onKeyDown(KeyDownEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					newArticle();
				}
			}
		});
	}

	private void setWatermark() {
		addArticleTextBox.addStyleName(WATERMARK_STYLE);
		addArticleTextBox.setText(ADD_ARTICLE_WATERMARK);
	}

	private void updateTotal() {
		Cart cart = getProjectService().getCategory(projectName, cartName);
		
		String total = NumberFormat.getCurrencyFormat().format(cart.getTotal());
		totalLabel.setContents("Total du panier : " + total);
		
		if (cart.getTotal() > cart.getBudget())
			totalLabel.setStyleName("redProductLabel");
		else
			totalLabel.setStyleName("productLabel");
	}

	@SuppressWarnings("unchecked")
	public void update(MObservable o, Object arg) {
		System.out.println("update");
		
		if (arg == null) {
			updateTotal();
			return;
		}
		if (arg instanceof List<?>) {
			for (Product p : (List<Product>)arg)
				addProductLine(p, true);
			return;
		}

		
		addProductLine((Product)arg);
	}

}
